﻿using System;
using System.Reflection;

using RpcClient.Attr;
using RpcClient.Helper;
using RpcClient.Interface;
using RpcClient.Model;
using RpcHelper;

using Unity;
using Unity.Lifetime;

namespace RpcClient.Collect
{

    /// <summary>
    /// IOC容器集合
    /// </summary>
    [Attr.ClassLifetimeAttr(ClassLifetimeType.单例)]
    internal class UnityCollect : IUnityCollect
    {
        /// <summary>
        /// IOC容器入口基类-手动注册使用
        /// </summary>
        //private static readonly Type _IContainer = typeof(IContainer);
      
        /// <summary>
        /// 忽略的属性类型
        /// </summary>
        private static readonly Type _IgnoreType = typeof(IgnoreIoc);

        private DefaultLifetimeType _DefLifetimeType;
        private ClassLifetimeType _Default = ClassLifetimeType.循环引用;

        private Type[] _DefFindInterface;

        private static readonly IUnityContainer _Container = null;
        /// <summary>
        /// IOC注册事件
        /// </summary>
        public event Register RegisterEvent;

        /// <summary>
        /// 注册前事件
        /// </summary>
        public event Registering Registering;

        static UnityCollect()
        {
            _Container = new UnityContainer();
        }
        public void SetDefFindInterface(Type[] types)
        {
            _DefFindInterface = types;
        }
        public void AddDefFindInterface(Type[] types)
        {
            _DefFindInterface = _DefFindInterface.Add(types);
        }
        public void SetDefLifetimeType(DefaultLifetimeType func)
        {
            this._DefLifetimeType = func;
        }
        public void SetDefLifetimeType(ClassLifetimeType type)
        {
            this._Default = type;
        }

        public bool Register(Type form, Type to)
        {
            if (this.RegisterType(form, to))
            {
                this.Load(to.Assembly);
                this.Register(to);
                return true;
            }
            return false;
        }
        public bool Register(Type form, Type to, string name)
        {
            if (this.RegisterType(form, to, name))
            {
                this.Load(to.Assembly);
                this.Register(to);
                return true;
            }
            return false;
        }
        public void Load(string assemblyName)
        {
            Assembly[] assembly = AppDomain.CurrentDomain.GetAssemblies();
            Assembly obj = assembly.Find(a => a.GetName().Name == assemblyName);
            if (obj != null)
            {
                this.Load(obj);
            }
        }
        public void Load(Assembly assembly, Type type)
        {
            Type[] types = assembly.GetTypes();
            Type[] classs = types.FindAll(a => a.IsClass);
            this._Register(type, classs);
        }
        public void Load(Assembly assembly)
        {
            Type[] types = assembly.GetTypes();
            Type[] inters = types.FindAll(a => a.IsInterface);
            Type[] classs = types.FindAll(a => a.IsClass);
            inters.ForEach(a =>
            {
                this._Register(a, classs);
            });
            _LoadDef(classs);
        }
        private void _LoadDef(Type[] classs)
        {
            if (_DefFindInterface != null)
            {
                _DefFindInterface.ForEach(c =>
                {
                    this._Register(c, classs);
                });
            }
        }

        /// <summary>
        /// 注册类型（递归扫描该类型下的所有构造函数类型自动注册）
        /// </summary>
        /// <param name="type"></param>
        public void Register(Type type)
        {
            ConstructorInfo[] argType = type.GetConstructors();
            Type[] allClass = type.Assembly.GetTypes().FindAll(a => a.IsClass);
            argType.ForEach(a =>
            {
                if (!a.IsPublic && !this._CheckIsReg(a))
                {
                    return;
                }
                ParameterInfo[] param = a.GetParameters();
                if (param.Length == 0)
                {
                    return;
                }
                param.ForEach(b =>
                {
                    if (b.ParameterType.Assembly.FullName == type.Assembly.FullName)
                    {
                        this._Register(b.ParameterType, allClass);
                    }
                    else
                    {
                        this._Register(b.ParameterType);
                    }
                });
            });
        }

        private void _Register(Type type)
        {
            Type[] allClass = type.Assembly.GetTypes().FindAll(a => a.IsClass);
            this._Register(type, allClass);
            this._LoadDef(allClass);
        }
        private bool _CheckIsReg(ConstructorInfo constructor)
        {
            ParameterInfo[] param = constructor.GetParameters();
            if (param.Length == 0)
            {
                return true;
            }
            return param.TrueForAll(a => a.ParameterType.IsInterface);
        }
        private bool _CheckIsReg(Type type, Type form)
        {
            if (type.GetInterface(form.FullName) == null || type.GetCustomAttribute(_IgnoreType) != null)
            {
                return false;
            }
            ConstructorInfo[] list = type.GetConstructors().FindAll(a => a.IsPublic && this._CheckIsReg(a));
            if (list.Length == 0)
            {
                return false;
            }
            return true;
        }
   
        /// <summary>
        /// 注册构造函数中的接口
        /// </summary>
        /// <param name="type"></param>
        private void _RegConstructor(Type type)
        {
            ConstructorInfo[] list = type.GetConstructors().FindAll(a => a.IsPublic && this._CheckIsReg(a));
            if (list.Length == 0)
            {
                return;
            }
            list.ForEach(c => {
                ParameterInfo[] param = c.GetParameters();
                if (!param.IsNull())
                {
                    param.ForEach(a => {
                        this.Register(a.ParameterType);
                    });
                }
            });
        }
        private bool _RegisterClass(Type form,ref Type[] to)
        {
            if (to.Length == 1)
            {
                return this.RegisterType(form, to[0]);
            }
            else
            {
                to = to.FindAll(c => {
                    string name = UnityHelper.GetUnityName(c);
                    if (name.IsNull())
                    {
                        if (c.BaseType.Name != "Object")
                        {
                            return false;
                        }
                        return this.RegisterType(form, c);
                    }
                    else
                    {
                        return this.RegisterType(form, c, name);
                    }
                });
                return to.Length > 0;
            }
        }

        private void _Register(Type form, Type[] classs)
        {
            if (form.GetCustomAttribute(_IgnoreType) != null)
            {
                return;
            }
            Type[] list = classs.FindAll(c => this._CheckIsReg(c, form));
            if (list.Length == 0)
            {
                return;
            }
            else if (this._RegisterClass(form,ref list))
            {
                list.ForEach(b => this.Register(b));
            }
        }
        private bool _Registering(IocBody body)
        {
            if (Registering != null)
            {
                return Registering(body);
            }
            return true;
        }
        private bool _IsRegistered(IocBody body)
        {
            if (body.Name.IsNull())
            {
                return _Container.IsRegistered(body.Form);
            }
            return _Container.IsRegistered(body.Form, body.Name);
        }
        private bool _RegisterType(IocBody body)
        {
            if (!this._Registering(body))
            {
                return false;
            }
            else if (this._IsRegistered(body))
            {
                return false;
            }
            this._RegConstructor(body.To);
            ITypeLifetimeManager lifetime = this._GetLifetime(body);
            if (!body.Name.IsNull())
            {
                _Container.RegisterType(body.Form, body.To, body.Name, lifetime);
            }
            else
            {
                _Container.RegisterType(body.Form, body.To, lifetime);
            }
            //if (body.To.GetInterface(_IContainer.FullName) != null)
            //{
            //    this._InitContainer(body.To);
            //}
            RegisterEvent(body);
            return true;
        }
        private ITypeLifetimeManager _GetLifetime(IocBody body)
        {
            ClassLifetimeType? type = UnityHelper.GetLifetimeType(body.Form, body.To);
            if (type.HasValue)
            {
                return UnityHelper.GetLifetime(type.Value);
            }
            else if (this._DefLifetimeType != null)
            {
                return UnityHelper.GetLifetime(this._DefLifetimeType(body));
            }
            else
            {
                return UnityHelper.GetLifetime(this._Default);
            }
        }
        private bool _RegisterInstance<T>(IocBody body, T data)
        {
            if (!this._Registering(body))
            {
                return false;
            }
            else if (this._IsRegistered(body))
            {
                return false;
            }
            this._RegConstructor(body.To);
            if (!body.Name.IsNull())
            {
                _Container.RegisterInstance<T>(body.Name, data);
            }
            else
            {
                _Container.RegisterInstance<T>(data);
            }
            RegisterEvent(body);
            //if (body.To.GetInterface(_IContainer.FullName) != null)
            //{
            //    this._InitContainer(body.To);
            //}
            return true;
        }
        private bool _RegisterInstance(IocBody body)
        {
            if (!this._Registering(body))
            {
                return false;
            }
            else if (this._IsRegistered(body))
            {
                return false;
            }
            this._RegConstructor(body.To);
            if (!body.Name.IsNull())
            {
                _Container.RegisterType(body.Form, body.To, body.Name, new ContainerControlledLifetimeManager());
            }
            else
            {
                _Container.RegisterType(body.Form, body.To, new ContainerControlledLifetimeManager());
            }
            RegisterEvent(body);
            //if (body.To.GetInterface(_IContainer.FullName) != null)
            //{
            //    this._InitContainer(body.To);
            //}
            return true;
        }
        private IocBody _GetIocBody(Type form, Type to, string name)
        {
            if (name.IsNull())
            {
                name = UnityHelper.GetUnityName(to);
            }
            return new IocBody(form, to, name);
        }
        public bool RegisterType(Type form, Type to)
        {
            return this._RegisterType(this._GetIocBody(form, to, null));
        }
        public bool RegisterType<T>(Type to)
        {
            return this._RegisterType(this._GetIocBody(typeof(T), to, null));
        }
        public bool RegisterType<T>(Type to, string name)
        {
            return this._RegisterType(this._GetIocBody(typeof(T), to, name));
        }
        /// <summary>
        /// 初始化化容器
        /// </summary>
        /// <param name="to"></param>
        //private void _InitContainer(Type to)
        //{
        //    if (_Container.IsRegistered(_IContainer, to.FullName))
        //    {
        //        return;
        //    }
        //    IUnityContainer unity = _Container.RegisterInstance(_IContainer, to.FullName, to);
        //    IContainer obj = (IContainer)unity.Resolve(_IContainer, to.FullName);
        //    obj.InitContainer(this);
        //}
        public bool RegisterInstance<T>(T data)
        {
            if (data == null)
            {
                return false;
            }
            return this._RegisterInstance<T>(this._GetIocBody(typeof(T), data.GetType(), null), data);
        }
        public bool RegisterInstance<T>(T data, string name)
        {
            if (data == null)
            {
                return false;
            }
            return this._RegisterInstance<T>(this._GetIocBody(typeof(T), data.GetType(), name), data);
        }
        public bool RegisterInstance(Type form, Type to, string name)
        {
            return this._RegisterInstance(this._GetIocBody(form, to, name));
        }
        public bool RegisterInstance(Type form, Type to)
        {
            return this._RegisterInstance(this._GetIocBody(form, to, null));
        }
        public bool RegisterType(Type form, Type to, string name)
        {
            return this._RegisterType(this._GetIocBody(form, to, name));
        }
        public object TryResolve(Type form)
        {
            if (!this.IsRegistered(form))
            {
                return null;
            }
            return _Container.Resolve(form);
        }
        public object TryResolve(Type form, string name)
        {
            if (!this.IsRegistered(form, name))
            {
                return null;
            }
            return _Container.Resolve(form, name);
        }
        public object Resolve(Type form)
        {
            return _Container.Resolve(form);
        }

        public object Resolve(Type form, string name)
        {
            if (name == null)
            {
                return _Container.Resolve(form);
            }
            return _Container.Resolve(form, name);
        }

        public T Resolve<T>()
        {
            return (T)this.Resolve(typeof(T));
        }
        public T TryResolve<T>()
        {
            return (T)this.TryResolve(typeof(T));
        }
        public T TryResolve<T>(string name)
        {
            return (T)this.TryResolve(typeof(T), name);
        }
        public T Resolve<T>(string name)
        {
            return (T)this.Resolve(typeof(T), name);
        }

        public void Load(Assembly assembly, Assembly Interfaces)
        {
            Type[] types = Interfaces.GetTypes();
            Type[] inters = types.FindAll(a => a.IsInterface);
            types = assembly.GetTypes();
            Type[] classs = types.FindAll(a => a.IsClass);
            inters.ForEach(a =>
            {
                this._Register(a, classs);
            });
            _LoadDef(classs);
        }

        public bool IsRegistered(Type type)
        {
            return _Container.IsRegistered(type);
        }

        public bool IsRegistered(Type type, string name)
        {
            return _Container.IsRegistered(type, name);
        }

        public bool RegisterType(Type form, Type to, ClassLifetimeType lifetimeType)
        {
            return this._RegisterType(new IocBody(form, to, lifetimeType));
        }

        public bool RegisterType(Type form, Type to, string name, ClassLifetimeType lifetimeType)
        {
            return this._RegisterType(new IocBody(form, to, name, lifetimeType));
        }

        public bool RegisterType<Form, To>()
        {
            return this._RegisterType(new IocBody(typeof(Form), typeof(To)));
        }

        public bool RegisterType<Form, To>(ClassLifetimeType lifetimeType)
        {
            return this._RegisterType(new IocBody(typeof(Form), typeof(To), lifetimeType));
        }
    }
}
